home *** CD-ROM | disk | FTP | other *** search
/ PC Professionell 2006 May / PCpro_2006_05.ISO / files / mobile / fma-2.0-stable-setup.exe / {app} / sframework / doc / dev.html < prev    next >
Encoding:
Extensible Markup Language  |  2004-09-09  |  34.3 KB  |  574 lines

  1. <?xml version="1.0" encoding="iso-8859-1"?>
  2. <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "DTD/xhtml1-transitional.dtd">
  3. <html>
  4.     
  5.     <head>
  6.         <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
  7.         <meta http-equiv="Expires"      content="Now" />
  8.         <meta http-equiv="Pragma"       content="No cache" />
  9.         <meta name="Description"        content="FMA Scripting Framework" />
  10.         <meta name="Keywords"           content="fma, float, mobile, agent, scripting, vbs, vbscript, framework, development, plugins, sony, ericsson, sonyericsson, t68i, t610" />
  11.         <meta name="Author"             content="Thomas Wittek <twittek@smail.uni-koeln.de>" />
  12.         <meta name="Robots"             content="nofollow" />
  13.         <!-- <link rel="StyleSheet" href="#fmadok.css" type="text/css" /> -->
  14.         <title>Developing own plugins</title>
  15.     </head>
  16.     
  17.     <body>
  18.         <h1>Developing own plugins</h1>
  19.         <h2>Table of contents</h2>
  20.         
  21.         <ul>
  22.             <li><a href="#simple"   >A simple plugin</a></li>
  23.             <li><a href="#menus"    >Menus</a></li>
  24.             <li><a href="#dialogues">Dialogues</a></li>
  25.             <li><a href="#control"  >Controlling applications</a></li>
  26.             <li><a href="#events"   >Events</a></li>
  27.             <li><a href="#keys"     >Key Events</a></li>
  28.             <li><a href="#settings" >Persistent Settings</a></li>
  29.             <li><a href="#adt"      >Abstract Data Types</a></li>
  30.             <li><a href="#util"     >Helpful utilities and Objects (Util, QuickSort, Debug, ...)</a></li>
  31.         </ul>
  32.         
  33.         <hr />
  34.         
  35.         <p>The FMA Scripting Framework was built do make the development of additional script functionality very easy. This is done by the offer of ready-to-use core classes, which will do all the "hard work" for you, and by a plugin-architecture with the goal, that a developer doesn't has to deal with all the other script files and may concentrate on the functionality itself instead of reinventing the wheel a thousand times.</p>
  36.         <p>It may be a good idea to set the debug log level to "DEBUG_LEVEL_DEBUG". This will log all debug messages to the Phone Explorer log. You can set the debug level by modifying the appropriate line in the "fma-scripting-framework.vbs" file:</p>
  37.         <p><code>Debug.SetDebugLevel DEBUG_LEVEL_DEBUG</code></p>
  38.         <p>All messages that are on a lever equal or greater then the current debug level will be logged in the Phone Explorer log. As DEBUG_LEVEL_DEBUG is the lowest level, every message will be logged.</p>
  39.         <p>Theses are the available debug levels (in ascending order):
  40.         <ul>
  41.             <li>DEBUG_LEVEL_DEBUG</li>
  42.             <li>DEBUG_LEVEL_INFO</li>
  43.             <li>DEBUG_LEVEL_WARN</li>
  44.             <li>DEBUG_LEVEL_ERROR</li>
  45.         </ul></p>
  46.         <p>There are also two "special" debug levels: <code>DEBUG_LEVEL_OFF</code> and <code>DEBUG_LEVEL_NOFMA</code>.</p>
  47.         <p>DEBUG_LEVEL_OFF will put no debug messages at all and DEBUG_LEVEL_NOFMA will use MsgBox'es instead of Phone Explorer logs. This may be useful, when checking the script for correctness outside of FMA e.g. when just double clicking the main script file and executing it in the Windows Scripting Host.</p>
  48.         <p>Additionally it is a good idea to keep a VBScript language referent by your hands. You can download a more or less comfortable verision from Microsoft: <a href="http://www.microsoft.com/downloads/details.aspx?FamilyID=01592c48-207d-4be1-8a76-1c4099d7bbb9&displaylang=en" target="_blank">Windows Script Documentation</a>.</p>
  49.         
  50.         <a name="#simple" />
  51.         <h2>A simple plugin</h2>
  52.         <p>Each script extension should be done through plugins. To add new functionality to the script you just have to create a new plugin file in the "plugins" folder. This file must be named "YourPlugin.vbs" and it must contain a class "YourPlugin".</p>
  53.         <p>Lets assume you want to create a plugin called "Simple". Then you have to create a file with the name "Simple.vbs" within the plugins folder. A minimum example could look like this:</p>
  54.         <p><code><pre>'FMA Script Framework Plugin
  55. 'Simple plugin
  56.  
  57. Class Simple
  58.     
  59.     Private m_Self 'Here your own name will be stored. We'll cover that later... See also Property Let/Get Self
  60.     
  61.     'Some info about the plugin
  62.     Public Property Get SHOWABLE 'Do I have a menu?
  63.         SHOWABLE    = True
  64.     End Property
  65.     Public Property Get TITLE 'What's my name? This will be the title of your menu entry in the main menu
  66.         TITLE       = "Simple plugin"
  67.     End Property
  68.     Public Property Get DESCRIPTION 'What's my purpose?
  69.         DESCRIPTION = "A minimum example for a plugin"
  70.     End Property
  71.     Public Property Get AUTHOR 'Who created me?
  72.         AUTHOR      = "streawkceur"
  73.     End Property
  74.     Public Property Get URL 'Were can I be found? Where can you get more information?
  75.         URL         = "http://fma.xinium.com/"
  76.     End Property
  77.     
  78.     'Who am I?
  79.     Public Property Let Self (s)
  80.         m_Self = s
  81.     End Property
  82.     Public Property Get Self
  83.         Self = m_Self
  84.     End Property
  85.     
  86.     'Show will be called every time the user selects your plugin from the main menu
  87.     Sub Show()
  88.         'Just put a log message to FMA
  89.         Debug.InfoMsg "Developing plugins is simple!"
  90.         'put an am.Update to prevent the menu from getting stuck
  91.         am.Update
  92.     End Sub
  93.  
  94. End Class</pre></code></p>
  95.         <p>Everytime you select your plugin from the main menu, there will appear a log message: <code>"[Script] Info: Developing plugins is simple!"</code></p>
  96.         <p>The next step would be to add <a href="#menus">menus</a> to your plugin.</p>
  97.         <p>You can also develop plugins, which aren't displayed at all in the phones menu. You just have to set <code>SHOWABLE = False</code> and you don't need the <code>Sub Show</code> anymore. Doing so, your plugin may only react on <a href="#events">Events</a> or <a href="#keys">Key Events</a>.</p>
  98.         
  99.         <a name="#menus" />
  100.         <h2>Menus</h2>
  101.         <p>You may easily add menus and submenus to your plugin. The framework will help you a lot with this. You don't have to care about the maximum count of menu items, seperating the menu into several pages and submenus. This will all be managed by the framework. You'll just have to define a list of menu items and the associated command.</p>
  102.         <p>So let's go! The creation of menus is done in by creating an object of the class ManagedMenu, setting up a menu list, the menu's title and showing it.</p>
  103.         <p>We can easily extend our Simple plugin with a menu: (don't get scared of the length of this code! 80% are comments)</p>
  104.         <p><code><pre>'FMA Script Framework Plugin
  105. 'Simple plugin
  106.  
  107. Class Simple
  108.     
  109.     Private m_Self '...
  110.     Private simpleMenu 'this variable will hold the ManagedMenu object
  111.     
  112.     'Some info about the plugin
  113.     '...
  114.     
  115.     'Who am I?
  116.     Public Property Let Self (s)
  117.         m_Self = s
  118.         'The Property Let Self is a good place to do some init work of the plugin
  119.         'since this one is called only once after object creation.
  120.         'Here you should initialize your menu, if it is a static one.
  121.         'If your menu has a dynamic nature, you should initialize it in Sub Show.
  122.         'Init the menu object:
  123.         Set simpleMenu = New ManagedMenu
  124.         'Create a menu list:
  125.         Dim tempList, backIns
  126.         Set tempList = New LinkedList 'The ManagedMenu accepts LinkedLists as menu lists
  127.         backIns = tempList.BackInserter 'backIns is the BackInserter of our created LList
  128.         'backIns.Item = <item> will append the item at the end of the LList.
  129.         'Each menu list entry must be an 2-element array with the entry's title and
  130.         'a command to execute on selection.
  131.         backIns.Item = Array( "first entry",  m_Self & ".First"  )
  132.         backIns.Item = Array( "second entry", m_Self & ".Second" )
  133.         'We used m_Self & ".First" as menu command, because m_Self is pointing to the
  134.         'variable name under which this plugin will be reached. ".First" is our object
  135.         'method. If you're creating a list outside the Property Let you may use
  136.         '"Self" instead of "m_Self". This may look better although the result would be
  137.         'the same
  138.         '...
  139.         'Now we push the list into out ManagedMenu object:
  140.         simpleMenu.SetList tempList
  141.         'And we set up the menus title:
  142.         simpleMenu.Title = TITLE 'TITLE is defined above
  143.     End Property
  144.     Public Property Get Self
  145.         '...
  146.     End Property
  147.     
  148.     '...
  149.     Sub Show()
  150.         'Here we should show our menu. It's fairly simple to do that:
  151.         simpleMenu.ShowMenu
  152.     End Sub
  153.     
  154.     Sub First()
  155.         Debug.DebugMsg "first item"
  156.         am.Update
  157.     End Sub
  158.     
  159.     Sub Second()
  160.         Debug.DebugMsg "second item"
  161.         am.Update
  162.     End Sub
  163.  
  164. End Class</pre></code></p>
  165.         <p>We're holding our menu in the variable <code>Private mainMenu</code>. You can hold it in every other variable, but it is a good idea to manage your menus in Class Members/Attributes. Especially if your plugin has more than one menu/submenus.</p>
  166.         <p>You should hold each menu of your plugin in one persistent variable. So a Class member/attribute would do best for this. Let's say your plugin has 3 menus: A Main menu and 2 submenus. The best would be to declare a Class member for each variable:</p>
  167.         <p><code><pre>    Private mainMenu
  168.     Private subMenu1
  169.     Private subMenu2</pre></code></p>
  170.         You should create each menu object only once:
  171.         <p><code><pre>    Public Property Let Self (s)
  172.         m_Self = s
  173.         
  174.         Set mainMenu = New ManagedMenu
  175.         Set subMenu1 = New ManagedMenu
  176.         Set subMenu2 = New ManagedMenu
  177.         'You might set the menu lists and the titles here. You might also do
  178.         'this e.g. in the Sub Show(), if your menu changes every time it is shown.
  179.         'Let's init them here:
  180.         Dim tempList, backIns
  181.         
  182.         'mainMenu
  183.         Set tempList = New LinkedList
  184.         backIns = tempList.BackInserter
  185.         backIns.Item = Array( "sub menu 1", "subMenu1.ShowMenu" )
  186.         backIns.Item = Array( "sub menu 2", "subMenu2.ShowMenu" )
  187.         mainMenu.SetList tempList
  188.         mainMenu.Title = "Main menu"
  189.         
  190.         'subMenu1
  191.         Set tempList = New LinkedList
  192.         backIns = tempList.BackInserter
  193.         backIns.Item = Array( "sm1 item 1", "Debug.DebugMsg ""sm1i1""" & vbCrLf & "am.Update" )
  194.         backIns.Item = Array( "sm1 item 2", "Debug.DebugMsg ""sm1i2""" & vbCrLf & "am.Update" )
  195.         subMenu1.SetList tempList
  196.         subMenu1.Title = "Submenu 1"
  197.         
  198.         'subMenu2
  199.         Set tempList = New LinkedList
  200.         backIns = tempList.BackInserter
  201.         backIns.Item = Array( "sm2 item 1", "Debug.DebugMsg ""sm2i1""" & vbCrLf & "am.Update" )
  202.         backIns.Item = Array( "sm2 item 2", "Debug.DebugMsg ""sm2i2""" & vbCrLf & "am.Update" )
  203.         subMenu2.SetList tempList
  204.         subMenu2.Title = "Submenu 2"
  205.     End Property
  206.     
  207.     Sub Show()
  208.         'Show the main menu
  209.         mainMenu.ShowMenu
  210.     End Sub</pre></code></p>
  211.         <p>This will allow for navigation through the menus by selecting them. You can go back to the last menu by pressing the back-button.</p>
  212.         <p>Another very useful feature of the ManagedMenu class is that is decides by itself how many items can be displayed at once. If you have more items, the ManagedMenu will automagically divide the menu into several pages between which you can navigate. It also checks that your titles are not longer than allowed and it escapes not allows characters in your entries and titles.</p>
  213.         <p>Since your menu is split into several pages, you might want to select a specific page in some cases. We've got <code>yourMenu.ShowPage <number></code> forthis purpose. You might also want to get a page number where a specific item resides. This can be done through <code>yourMenu.GetPageByTitleSubstr(<title substring>)</code>. If you also want to get the position of this item on the page you might want to use<code><an array> = yourMenu.GetPageAndIndexByTitleSubstr(<title substring>)</code>. You can also get the page from an absolute item number: <code>yourMenu.GetPageByItemIndex</code>. And we also have <code>yourMenu.GetPageAndIndexByItemIndex</code>. All this is only used in the Winamp plugin, but it might be handy for other cases.</p>
  214.         <p>Warning: If you're generating menus on the fly from some data you've got, make sure that you don't display "empty" menus (without any item). This one's won't be displayed at all, which may not be what you want. So check if you've generated an empty list and in this case put a dummy item into the list. For example:<code>If tempList.Count < 1 Then tempList.AddBack Array( "(empty)", "am.Update" )</code>.</p>
  215.         
  216.         <a name="#dialogues">
  217.         <h2>Dialogues</h2>
  218.         <p>Using dialogues isn't much different than in "non-framework" scripts.</p>
  219.         <p>But there's one thing you should care about. When you put a dialogue the user may quit it with the back-button or the dialogue may time out. In this case FMA executes what is stored in <code>am.Back</code>. As the am.Back content is generally managed by the <code>ManagedMenu</code>-class, you should "show" an empty "dummy" menu before displaying a dialogue.</p>
  220.         <p>Example:</p>
  221.         <p><code><pre>    EmptyMenu.ShowMenu
  222.     am.DlgMsgBox "Demo text", 5</pre></code></p>
  223.         <p>So when the user quits the dialogue with the back-button or the dialogue times out (5 seconds), the empty "dummy" menu will be quit and the previously displayed menu will be shown.</p>
  224.         <p>You could also manage the back-button yourself like this:</p>
  225.         <p><code><pre>    am.Back = "am.Update"
  226.     am.DlgMsgBox "Demo text", 5</pre></code></p>
  227.         <p>This will look like the same on the first look. If the user quits ot the dialogue times out, the previous menu will be shown. But the problem with this would be, that the back-button management will be messed up and <code>am.Back</code> will alway be <code>"am.Update"</code> and you cannot quit to previous menus.</p>
  228.         <p>So if you want to control by yourself what happens, when the user quits the dialogue or the dialogue times out, then you should do it like this:</p>
  229.         <p><code><pre>    am.Back = Self & ".DlgQuit" 'Mangage the menu quit by ourselves
  230.     am.DlgMsgBox "Demo text", 5 'Put the dialogue
  231.     Sub DlgQuit
  232.         Debug.DebugMsg "Dialogue quitted or timed out" 'Here you can do some stuff...
  233.         MenuStack.Top.Show 'Show the last menu that was displayed before the dialoge. This will also set am.Back correctly
  234.     End Sub</pre></code></p>
  235.         <p>By doing so, you'll have an other problem: The joy-left and joy-right keys are registered for swapping the pages of the top most menu. So when your dialogue will be quitted, when the user pushes the joy-left/-right keys as this will display the prev/next page of the top most menu (, which is the menu you showed before the dialogue). So you might want to use a combination of controlling am.Back and an EmptyMenu:</p>
  236.         <p><code><pre>    EmptyMenu.ShowMenu 'Push empty menu to prevent page swapping of the last menu
  237.     am.Back = Self & ".DlgQuit" 'Mangage the menu quit by ourselves
  238.     am.DlgMsgBox "Demo text", 5 'Put the dialogue
  239.     Sub DlgQuit
  240.         Debug.DebugMsg "Dialogue quitted or timed out" 'Here you can do some stuff...
  241.         MenuStack.Top.Quit 'Remove emtpy menu
  242.     End Sub</pre></code></p>
  243.         <p>A good point to get an idea of how to work with dialogues would be the Demo plugin (Demo.vbs). Almost everything you can do with dialoges is shown here and well documented.</p>
  244.         
  245.         <a name="#control" />
  246.         <h2>Controlling applications</h2>
  247.         <p>One of the main purposes of the FMA scipts is to allow the control of windows applications (Like media players etc.) from your phone.</p>
  248.         <p>There are two easy ways to control applications: The first one sends key strokes to the specific application that will cause an action in the application. The other one controls the application directly via an API/COM objects.</p>
  249.         <p>The procedure will be fairly similar: First you have to activate your application (put it to the foreground). If this fails, in most cases the reason would be that the application is not running yet, so you eventually have to launch the application. If your application is launched, you can start controlling it by sending key strokes or API commands.</p>
  250.         <p>Just look at this simple example, which will explain the detailed steps:</p>
  251. <p><code><pre>Class Simple
  252.     
  253.     Private m_Self
  254.     Private simpleMenu
  255.     Private appTitle, appPath
  256.     
  257.     'Some info about the plugin
  258.     '...
  259.     
  260.     'Who am I?
  261.     Public Property Let Self (s)
  262.         m_Self = s
  263.         
  264.         'We'll init the menu object herem but the list will be generated everytime 
  265.         'Sub Show() is called as we want to have a dynamic list.
  266.         Set simpleMenu = New ManagedMenu
  267.         simpleMenu.Title = "App Control"
  268.         'We need the window title and the path of the application we want to control
  269.         appTitle = "Calculator"
  270.         appPath  = "calc.exe"
  271.     End Property
  272.     Public Property Get Self
  273.         Self = m_Self
  274.     End Property
  275.     
  276.     'When our plugin shall show its menu, we will dynamically generate the list
  277.     'and display it
  278.     Sub Show()
  279.         Dim tempList, backIns
  280.         Set tempList = New LinkedList
  281.         backIns = tempList.BackInserter
  282.         If AppOpen Then 'We could activate our app. Display the menu:
  283.             backIns.Item = Array( "Calulate stuff", m_Self & ".CalcStuff" )
  284.             backIns.Item = Array( "Close",          m_Self & ".Close" )
  285.         Else 'Our app seems not to be loaded yet. Allow for launching it:
  286.             backIns.Item = Array( "Launch",         m_Self & ".Launch" )
  287.         End If
  288.         simpleMenu.SetList tempList
  289.         simpleMenu.ShowMenu
  290.     End Sub
  291.     
  292.     Function AppOpen
  293.         AppOpen = Shell.AppActivate(appTitle)
  294.     End Function
  295.     
  296.     Sub Launch
  297.         Shell.Run appPath
  298.         'Give our app max. 5 secs to load. If we wouldn't wait here, the menu would
  299.         'show "Launch" again, since the app had no time to load until the menu is shown again
  300.         Util.WaitForAppLoad appTitle, 6000
  301.         Show
  302.     End Sub
  303.     
  304.     Sub CalcStuff
  305.         If AppOpen Then
  306.             'We can use the Shell.SendKeys Method to send some key strokes to the application:
  307.             Shell.SendKeys "1{+}2~"
  308.             'Should be 3
  309.         End If
  310.         am.Update
  311.     End Sub
  312.     
  313.     Sub Close
  314.         If AppOpen Then
  315.             Shell.SendKeys "%{F4}"
  316.             'Give our app max. 5 secs to close itself. If we wouldn't wait here, the menu wouldn't
  317.             'show "Launch" again, since the app had no time to load until the menu is shown again
  318.             Util.Sleep 3000
  319.         End If
  320.         Show
  321.     End Sub
  322.     
  323. End Class</pre></code></p>
  324.         <p>If you'd like to control your application through ActiveX/COM objects (like FMA's Winamp COM control, or floAt's Media Control, ...) you have to create this object an call it's methods instead of sending keys to that application. The script wouldn't look much different. It's most likely that only there will be COM control object method calls instead of the Shell.SendKeys lines.</p>
  325.         <p>If you plan to use such external objects you should use the ActiveXManager instead of creating the objects yourself. This will ensure that the objects are only created once and not 10 times when 10 plugins use this object:</p>
  326.         <p><code><pre>    'Mute your speakers:
  327.     ActiveXManager("floAtMediaCtrl.VolumeCtrl").mute = 1</code></pre></p>
  328.         <p>This will internally return the object and create it if not done already. So there will only be one instance of this object that can be shared with all plugins.</p>
  329.         
  330.         <a name="#events" />
  331.         <h2>Events</h2>
  332.         <p>All event handling is done via the EventManger object. You can register commands for a specific event, and also deregister your registered commands. You can even fire own events in a simple fashion.</p>
  333.         <p>Plugins may register for events by doing:</p>
  334.         <p><code><pre>    Set item = EventManager.RegisterEvent( "<event>", "<command to execute>", <plugin-object> )</pre></code></p>
  335.         <p>Example:</p>
  336.         <p><code><pre>    Dim item
  337.     Set item = EventManager.RegisterEvent( "Connected", "Debug.DebugMsg ""just connected""", Me )</pre></code></p>
  338.         <p>This will register the command <code>Debug.DebugMsg "just connected"</code> for the event "Connected" and from the plugin "Me" (which is the object from which you are calling) and save a reference to the event in the variable "item".</p>
  339.         <p>This way you can easily deregister this event by doing:</p>
  340.         <p><code><pre>    item.Delete</pre></code></p>
  341.         <p>You may even collect event groups in LinkedLists and deregister them all at once:</p>
  342.         <p><code><pre>    Dim item, ll, bi
  343.     Set ll = New LinkedList
  344.     bi = ll.BackInserter
  345.     Register:
  346.     Set bi.Item = EventManager.RegisterEvent( "Connected",      "Debug.DebugMsg ""just connected""",    Me )
  347.     Set bi.Item = EventManager.RegisterEvent( "Disconnected",   "Debug.DebugMsg ""just disconnected""", Me )
  348.     Set bi.Item = EventManager.RegisterEvent( "ConnectionLost", "Debug.DebugMsg ""connection lost!""",  Me )
  349.     Deregister:
  350.     EventManager.DeregisterAllFromLL ll</code></pre></p>
  351.         <p>If you don't want to deregister like this, you can just call:</p>
  352.         <p><code><pre>    EventManager.RegisterEvent "Connected", "Debug.DebugMsg ""just connected""", Me</pre></code></p>
  353.         <p>without saving the reference. Then you can only deregister with DeregisterAll.</p>
  354.         <p>Plugins may deregister ALL their registered events at once by passing an object reference to themselves:</p>
  355.         <p><code><pre>    EventManager.DeregisterAll Me</pre></code></p>
  356.         <p>Plugins may even throw own events by simply doing:</p>
  357.         <p><code><pre>    EventManager.OnEvent <event>, Array([<argument1>, [<argument2>, [...]]])</pre></code></p>
  358.         <p>The following Script Events are triggered by FMA and handled by the EventManager under this names: (copied from <a href="http://www.fma.xinium.com/resources/docs/scripts_sdk.htm">http://www.fma.xinium.com/resources/docs/scripts_sdk.htm</a>)</p>
  359.         <ul>
  360.             <li>AMRoot</li>
  361.             <li>Init</li>
  362.             <li>Connected</li>
  363.             <li>ConnectionLost</li>
  364.             <li>Disconnected</li>
  365.             <li>NewSMS</li>
  366.             <li>Call</li>
  367.             <li>Proximity</li>
  368.             <li>MusicMute</li>
  369.             <li>KeyPress</li>
  370.         </ul>
  371.         
  372.         <a name="#keys" />
  373.         <h2>Key Events</h2>
  374.         <p>Key events are quite similar to the "normal" events instead of some small differences. First you have to pass the key state (press/release) to the KeyManager and second the KeyManager will automagically enable and disable KeyMonitoring in the case there are registered key events or not.</p>
  375.         <p>Plugins may register for keys by doing:</p>
  376.         <p><code><pre>    Set item = KeyManager.RegisterKey( "<key>", "<command to execute>", <state>, <plugin-object> )</pre></code></p>
  377.         <p>Example:</p>
  378.         <p><code><pre>    Dim item
  379.     Set item = KeyManager.RegisterKey( KEY_JOYPRESS, "Debug.DebugMsg ""joystick pressed""", STATE_PRESS, Me )</pre></code></p>
  380.         <p>This will register the command <code>Debug.DebugMsg "joystick pressed"</code> for key down ":J" and from the plugin "Me" (which is the object from which you are calling) and save a reference to the event in the variable "item".</p>
  381.         <p>This way you can easily deregister this key-event by doing:</p>
  382.         <p><code><pre>    item.Delete</pre></code></p>
  383.         <p>You may even collect key groups in LinkedList's and deregister them all at once:</p>
  384.         <p><code><pre>    Dim item, ll, bi
  385.     Set ll = New LinkedList
  386.     bi = ll.BackInserter
  387.     'Register:
  388.     Set bi.Item = KeyManager.RegisterKey( KEY_JOYPRESS,  "Debug.DebugMsg ""joystick pressed""",      STATE_PRESS, Me )
  389.     Set bi.Item = KeyManager.RegisterKey( KEY_SOFTLEFT,  "Debug.DebugMsg ""left softkey pressed""",  STATE_PRESS, Me )
  390.     Set bi.Item = KeyManager.RegisterKey( KEY_SOFTRIGHT, "Debug.DebugMsg ""right softkey pressed""", STATE_PRESS, Me )
  391.     'Deregister:
  392.     KeyManager.DeregisterAllFromLL ll</pre></code></p>
  393.         <p>If you don't want to deregister like this, you can just call:</p>
  394.         <p><code><pre>    KeyManager.RegisterKey KEY_JOYPRESS, "Debug.DebugMsg ""joystick pressed""", STATE_PRESS, Me</pre></code></p>
  395.         <p>without saving the reference. Then you can only deregister with DeregisterAll.</p>
  396.         <p>Plugins may deregister ALL their registered keys at once by passing an object reference to themselves:</p>
  397.         <p><code><pre>    KeyManager.DeregisterAll Me</pre></code></p>
  398.         <p>Plugins may even throw own key events by simply doing:</p>
  399.         <p><code><pre>    KeyManager.OnKey <key></pre></code></p>
  400.         <p>You can use the following constants within you code:</p>
  401.         <p><code><pre>    KEY_JOYUP     = "^"
  402.     KEY_JOYDOWN   = "v"
  403.     KEY_JOYLEFT   = "<"
  404.     KEY_JOYRIGHT  = ">"
  405.     KEY_JOYPRESS  = ":J"
  406.     KEY_BACK      = ":R"
  407.     KEY_C         = "c"
  408.     KEY_SOFTLEFT  = "["
  409.     KEY_SOFTRIGHT = "]"
  410.     KEY_HOME      = ":O"
  411.     KEY_CAMERA    = ":C"
  412.     KEY_VOLUP     = "u"
  413.     KEY_VOLDOWN   = "d"
  414.     KEY_0         = "0"
  415.     KEY_1         = "1"
  416.     KEY_2         = "2"
  417.     KEY_3         = "3"
  418.     KEY_4         = "4"
  419.     KEY_5         = "5"
  420.     KEY_6         = "6"
  421.     KEY_7         = "7"
  422.     KEY_8         = "8"
  423.     KEY_9         = "9"
  424.     KEY_ASTERIX   = "*"
  425.     KEY_SHARP     = "#"
  426.     KEY_YES       = "s" 'T68i
  427.     KEY_NO        = "e" 'T68i
  428.     KEY_OPT       = "f" 'T68i
  429.     
  430.     STATE_PRESS   = 1
  431.     STATE_RELEASE = 0</pre></code></p>
  432.         
  433.         <a name="#settings" />
  434.         <h2>Persistent Settings</h2>
  435.         <p>It's not a good idea to use hard-coded settings like application paths and so on. It also wouldn't be a very good idea letting each plugin manage an own settings file. This would be hard to maintain both for developers and users. So we introduced the <code>Settings</code>-object.</p>
  436.         <p>Its use is fairly simple.</p>
  437.         <p>If you want to save a setting you may just call:</p>
  438.         <p><code><pre>    Settings( Me, "<your setting>") = <your value></pre></code></p>
  439.         <p>To retrieve your saved setting, you simply call:</p>
  440.         <p><code><pre>    variable = Settings( Me, "<your setting>")</pre></code></p>
  441.         <p>This will assign the value <code>Empty</code> to your variable, when there is no setting with that key yet.</p>
  442.         <!-- <p>Note that it is a good idea to structure your settings into "folders". Lets say your plugin is called "YourPlugin". You should use the following structure:</p>
  443.         <p><code><pre>    Settings("YourPlugin\Key1") = <your value>
  444.     Settings("YourPlugin\Key2\Subkey") = <your value>
  445.     ...</pre></code></p>
  446.         <p>This will prevent that you modify settings from other plugins and it will also help our configuration application to build a beautiful tree structure of the settings.</p> -->
  447.         <p>Using <code>Me</code> as first argument prevents from reading or writing settings of other plugins.</p>
  448.         <p>But you may also access settings without passing an object reference to yourself:</p>
  449.         <p><code><pre>    Settings.Setting("<your setting>") = <your value>
  450.     variable = Settings.Setting("<your setting>")</pre></code></p>
  451.         <p>The settings will be loaded when OnInit and OnConnected is called. They will be saved when OnDisconnected and OnConnectionLost is called.</p>
  452.         
  453.         <a name="#adt" />
  454.         <h2>Abstract Data Types</h2>
  455.         <p>To enable a more easy way of handling data in VBScript we introduced some abstract data types (ADT).</p>
  456.         <p>In detail we've got the following ones:</p>
  457.         <ul>
  458.             <li>Hash</li>
  459.             <li>LinkedList</li>
  460.             <li>Stack</li>
  461.         </ul>
  462.         <p>The Hash is used similar to the settings object:</p>
  463.         <p><code><pre>    Dim h, v, o
  464.     Set h = New Hash 'Create object
  465.     h("<key>") = <value> 'assign value
  466.     v = h("<key>") 'retrieve value
  467.     'You may also assign and retrieve objects:
  468.     Set h("<key>") = <object> 'assign object
  469.     Set o = h("<key>") 'retrieve object</pre></code></p>
  470.         <p>The Hash has also some useful methods:</p>
  471.         <p><code><pre>    hash.DeleteItem( <key> )              'delete an entry
  472.     <array>      = hash.GetKeysArray      'retrieve an unsorted list of all keys to the items stored in the hash
  473.     <LinkedList> = hash.GetKeysLinkedList 'retrieve an unsorted LinkedList of all keys to the items stored in the hash
  474.     hash.LoadFromFile( fileName )         'load the hash'es content from a file
  475.     hash.SaveToFile( fileName )           'save the content to a file</pre></code></p>
  476.         <p>The LinkedList has a rather big interface, so I will just demonstrate a subset of its features. If you want to know more, you might take a look into the well documented source code of the LinkedList class.</p>
  477.         <p><code><pre>    Dim ll, bi, it
  478.     Set ll = New LinkedList 'create LinkedList
  479.     bi = ll.BackInserter 'the BackInserter will append each assigned item at the end of the LinkedList
  480.     bi.Item = 1
  481.     bi.Item = 2
  482.     bi.Item = 3
  483.     bi.Item = 4
  484.     it = ll.Begin 'we will start at the first item
  485.     Do Until it.Object Is ll.Last.Object 'and iterate until the last one is reached
  486.         MsgBox it.Item 'retrieve and put out the value
  487.         it.iterate() 'go to the next item
  488.     Loop</pre></code></p>
  489.         <p>The Stack has a rather small interface and is used exactly as you would expect it:</p>
  490.         <p><code><pre>    Dim s
  491.     Set s = New Stack 'create the stack
  492.     s.Push <value> 'push a value on the top of the stack
  493.     s.Push <object> 'push an object on the top of the stack
  494.     MsgBox IsObject(s.Top) 'the top method will return the item on the top of the stack
  495.     MsgBox IsObject(s.Pop) 'the pop method will return the item on the top of the stack and remove it
  496.     s.Pop
  497.     MsgBox s.IsEmpty 'will return a boolean value. True if there are no items on the stack, False otherwise</pre></code></p>
  498.         
  499.         <a name="#util" />
  500.         <h2>Helpful utilities (Util, QuickSort, Debug)</h2>
  501.         <p>We've also developed some useful utility classes that you may access through global variables.</p>
  502.         <p>First of all I would like to mention the <code>Util</code> object. It offers the following methods and functions:</p>
  503.         <p><code><pre>    'let the script sleep for the passed amount of milliseconds:
  504.     Util.Sleep <milliseconds>
  505.     
  506.     'will put his text on the standby screen of your phone
  507.     Util.SetStandbyScreenText "<some text>"
  508.     
  509.     'let the script sleep until an application with a specified title is
  510.     'loaded (i.e. activated) or until a time limit is reached.
  511.     'Timeout of 0 ms will wait forever:
  512.     Util.WaitForAppLoad <title>, <milliseconds>
  513.     
  514.     'let the script sleep until an application with a specified title is
  515.     'closed (i.e. cannot be activated) or until a time limit is reached
  516.     'Timeout of 0 ms will wait forever:
  517.     Util.WaitForAppClose <title>, <milliseconds>
  518.     
  519.     'currently this ones are only used internally in our hash class for loading and saving.
  520.     'this functions will (un)escape line breaks within a given string:
  521.     Dim text, escaped, unescaped
  522.     text      = <some text>
  523.     escaped   = EscapeLinebreaks text
  524.     unescaped = UnescapeLinebreaks escaped
  525.     MsgBox text = unescaped 'should be True</pre></code></p>
  526.         <p>As there was no built-in function in VBScript so comfortably sort data, we've developed a flexible QuickSort class. You may pass a Comparator (which decides <emph>how</emph> to sort) and a Data object (which decides <emph>what</emph> to sort). You may also just use the built-in default Comparator- and Data-Objects and don't have to care about these classes/objects.</p>
  527.         <p>Within the framework you may use the global object <code>QuickSorter</code>:</p>
  528.         <p><code><pre>
  529.     QuickSorter.Sort( array )                                      'will sort the array with the default comparator
  530.     QuickSorter.SortLL( LList )                                    'will sort the LList with the default comparator
  531.     QuickSorter.ComparatorSort( array, ComparatorObject )          'will sort the array with a given comparator
  532.     QuickSorter.ComparatorSortLL( LList, ComparatorObject )        'will sort the LList with a given comparator
  533.     QuickSorter.DataSort( DataObject )                             'will sort by using the given DataObject with the default comparator
  534.     QuickSorter.DataComparatorSort( DataObject, ComparatorObject ) 'will sort by using the given DataObject with a given comparator</pre></code></p>
  535.         <p>Most likely you will only use Sort and SortLL with default comparator and data objects, but if you want a special order or if you want to sort an array/lis of objects you might want to develop modified comparator and data classes.</p>
  536.         <p>We've also got a global <code>Debug</code>-object, which we already introduced at the beginning of this chapter. It offers the possibility to log messages with several log levels. This has the advantage, that you might want to see all log messages when developing the script, but you don't want to confuse the user with all your debug messages when using the script.</p>
  537.         <p>It may be a good idea to set the debug log level to "DEBUG_LEVEL_DEBUG". This will log all debug messages to the Phone Explorer log. You can set the debug level by modifying the appropriate line in the "fma-scripting-framework.vbs" file:</p>
  538.         <p><code>Debug.SetDebugLevel DEBUG_LEVEL_DEBUG</code></p>
  539.         <p>All messages that are on a lever equal or greater then the current debug level will be logged in the Phone Explorer log. As DEBUG_LEVEL_DEBUG is the lowest level, every message will be logged.</p>
  540.         <p>Theses are the available debug levels (in ascending order):
  541.         <ul>
  542.             <li>DEBUG_LEVEL_DEBUG</li>
  543.             <li>DEBUG_LEVEL_INFO</li>
  544.             <li>DEBUG_LEVEL_WARN</li>
  545.             <li>DEBUG_LEVEL_ERROR</li>
  546.         </ul></p>
  547.         <p>There are also two "special" debug levels: <code>DEBUG_LEVEL_OFF</code> and <code>DEBUG_LEVEL_NOFMA</code>.</p>
  548.         <p>DEBUG_LEVEL_OFF will put no debug messages at all and DEBUG_LEVEL_NOFMA will use MsgBox'es instead of Phone Explorer logs. This may be useful, when checking the script for correctness outside of FMA e.g. when just double clicking the main script file and executing it in the Windows Scripting Host.</p>
  549.         <p>To put a log message for a specific log level on the Phone Explorer log you may call this methods:</p>
  550.         <p><code><pre>    ErrorMsg "<text>"
  551.     WarningMsg "<text>"
  552.     InfoMsg "<text>"
  553.     DebugMsg "<text>"</pre></code></p>
  554.         <p>You may also access the global <code>PluginManager</code>-object, but I won't describe it here, since it shouldn't be of any use as a plugin developer. It's rather used in the frameworks internals.</p>
  555.         <p>Additionally we've got the two global objects <code>Fso</code>, which is a Scripting.FileSystemObject object and can be used to do all kinds of filehandling, and <code>Shell</code>, which is a WScript.Shell object and can be used to launch applicationd, activate them and sending key codes to them.</p>
  556.         <p>Notice that you can use two different methods to launch an application:</p>
  557.         <p><code><pre>    Shell.Run "<app>"
  558.     'will execute any application within the PATH environment variable, but it only
  559.     'accepts MSDOS 8.3 filenames if you want to run an application where you have a full path
  560.     
  561.     Shell.Exec "<app>"
  562.     'this one should be used when you have full paths. Shell.Exec accepts long filenames,
  563.     'but it cannot run applications in the PATH environment variable</pre></code></p>
  564.         <p>To control your applications you may use the following methods, which we already introduced <a href="#control">above</a>:</p>
  565.         <p><code><pre>    success = Shell.AppActivate("<app-title>")
  566.     Shell.SendKeys "<key sequence>"</pre></code></p>
  567.         <p>A full reference of the key sequences can be found in the <a href="http://www.microsoft.com/downloads/details.aspx?FamilyID=01592c48-207d-4be1-8a76-1c4099d7bbb9&displaylang=en" target="_blank">Windows Script Documentation</a>.</p>
  568.         
  569.         <hr />
  570.         by <a href="http://www.zentrifuge.biz/">streawkceur</a>. <a href="http://fma.xinium.com/">http://fma.xinium.com/</a>
  571.     </body>
  572.     
  573. </html>
  574.